/*
 * Copyright 2011, 2012 Fabian Wenzelmann
 * 
 * This file is part of Recognition-Procedures-for-Boolean-Functions.
 * 
 * Recognition-Procedures-for-Boolean-Functions is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Recognition-Procedures-for-Boolean-Functions is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Recognition-Procedures-for-Boolean-Functions.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.wenzelmf.booleanFunction.lpb.combinatorial.splittingTree;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * A node usually does not have some global information about the tree it is contained in, this class
 * helps node to access other nodes in the tree.
 * 
 * A tree context has references to all nodes in the tree what is sometimes necessary.
 * 
 * @author Fabian Wenzelmann
 * @version 1.0
 *
 */
public class TreeContext implements Serializable, Iterable<Iterator<SplittingTreeNode>>
{
	private static final long serialVersionUID = -1576260603629945779L;
	
	/*
	 * Store all nodes of the tree.
	 */
	protected ArrayList< ArrayList<SplittingTreeNode> > tree;
	
	/**
	 * Construct a new and empty tree context.
	 */
	public TreeContext()
	{
		this.tree = new ArrayList< ArrayList<SplittingTreeNode> >();
	}
	
	/**
	 * Construct a new and empty tree context given the depth of the tree (columns in the
	 * table) which is more efficient for the array generation.
	 * 
	 * @param columns The depth of the table (columns in the table), if this value is smaller than
	 * the real value that's ok, it's only for efficiency reasons.
	 */
	public TreeContext(int columns)
	{
		this.tree = new ArrayList< ArrayList<SplittingTreeNode> >(columns);
	}
	
	/**
	 * Return an iterator over all nodes in the specified column.
	 * 
	 * However the iterator supports all methods (such as remove), but you should not use them...
	 * 
	 * @param column The column in the table.
	 * 
	 * @return An iterator over all nodes in the specified column.
	 * 
	 * @throws IndexOutOfBoundsException If the table does not have the specified column.
	 */
	public Iterator< SplittingTreeNode > getColumn(int column)
	{
		return tree.get(column).iterator();
	}
	
	/**
	 * Return the node in the specified column and row.
	 * 
	 * @param row The row in the table.
	 * @param column The column in the table.
	 * 
	 * @return The node in the specified column and row of the table.
	 * 
	 * @throws IndexOutOfBoundsException If there is no element on the specified position in the table.
	 */
	public SplittingTreeNode get(int row, int column)
	{
		return tree.get(column).get(row);
	}
	
	/**
	 * Add a splitting tree node to this tree context.
	 * 
	 * The column of the node is determined through the node.
	 * The node is added to the end of this column.
	 * If you add a node in column <i>n</i> and the last node that you have added to this tree context
	 * was in row <i>n - 2</i> this node will be added in row <i>n - 1</i>, not <i>n</i>!
	 * 
	 * TODO: make method better? (define a position maybe?)
	 * 
	 */
	// add nodes in the right column
	public int addNode(SplittingTreeNode n)
	{
		Integer column = n.getColumn();
		if(column == null)
			throw new NullPointerException("Column number of a node is not allowed to be null.");
		
		ArrayList<SplittingTreeNode> c = null;
		try
		{
			c = tree.get(column);
		}
		catch(IndexOutOfBoundsException e)
		{
			c = new ArrayList<SplittingTreeNode>();
			tree.add(c);
		}
		c.add(n);
		return c.size() - 1;
	}
	 
	/**
	 * Returns an iterator over all columns in the tree, i. e. it returns
	 * {@link #getColumn(int)} for all columns in the tree.
	 * 
	 * @return An iterator over all columns in the tree
	 * 
	 * @see #getColumn(int)
	 */
	public Iterator<Iterator<SplittingTreeNode>> iterator()
	{
		return new Iterator<Iterator<SplittingTreeNode>>()
		{
			Iterator<ArrayList<SplittingTreeNode>> it = tree.iterator();
			int pos = 0;
			
			@Override
			public boolean hasNext()
			{
				return it.hasNext();
			}

			@Override
			public Iterator<SplittingTreeNode> next()
			{
				it.next();
				return getColumn(pos++);
			}

			@Override
			public void remove() throws UnsupportedOperationException
			{
				throw new UnsupportedOperationException("Removing elements from a TreeContext is not allowed." );
			}
			
		};
	}
	
	/**
	 * Returns the amount of columns in the table.
	 * 
	 * @return How many columns are in the table.
	 */
	public int size()
	{
		return tree.size();
	}
	
	/**
	 * Return the amount of rows in the specified column.
	 * 
	 * @param column The column in the table.
	 * 
	 * @return The size (rows) in the specified column.
	 */
	public int getColumnSize(int column)
	{
		return tree.get(column).size();
	}
}
